// Code file original author: Martin Lapierre, http://devinstinct.com

using System;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
using Microsoft.Practices.EnterpriseLibrary.Validation;
using System.Globalization;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using EntLibContrib.Validation.Configuration;

namespace EntLibContrib.Validation.Validators
{
    /// <summary>
    /// Validates that a value is of, or inherits, a specific type.
    /// </summary>
    /// <remarks>
    /// A null value is not considered to be of the same type (convention of the 'is' operator).
    /// By following that convention we make sure that null won't be valid for value-types such as DateTime.
    /// This validator is mainly used for downcasting.
    /// </remarks>
    /// <typeparam name="T">The target type.</typeparam>
	[ConfigurationElementType(typeof(TypeValidatorData))]
	public class TypeValidator<T> :
        ValueValidator<T>
    {
        #region Constructors

        /// <summary>
        /// Default constructor.
        /// </summary>
        public TypeValidator()
            : base(null, null, false)
        {
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="messageTemplate">
        /// The template to use when logging validation results, 
        /// or null we the default message template is to be used.
        /// </param>
        /// <param name="tag">
        /// The tag to set when logging validation results, or null.
        /// </param>
        /// <param name="negated">
        /// Indicates if the validation logic represented by the validator should be negated.
        /// </param>
        public TypeValidator(string messageTemplate, string tag, bool negated)
            : base(messageTemplate, tag, negated)
        {
        }

        #endregion Constructors


        #region Properties

        /// <summary>
        /// Gets the Default Message Template when the validator is negated.
        /// </summary>
        protected override string DefaultNegatedMessageTemplate
        {
            get 
            { 
                return Resources.TypeValidatorNegatedDefaultMessageTemplate; 
            }
        }

        /// <summary>
        /// Gets the Default Message Template when the validator is non negated.
        /// </summary>
        protected override string DefaultNonNegatedMessageTemplate
        {
            get
            {
                return Resources.TypeValidatorNonNegatedDefaultMessageTemplate;
            }
        }

        #endregion Properties


        #region Methods

        /// <summary>
        /// Implements the validation logic for the receiver.
        /// </summary>
        /// <param name="objectToValidate">The instance of T to validate.</param>
        /// <param name="currentTarget">The object on the behalf of which the validation is performed.</param>
        /// <param name="key">The key that identifies the source of objectToValidate.</param>
        /// <param name="validationResults">The validation results to which the outcome of the validation should be stored.</param>
        protected override void DoValidate(T objectToValidate, object currentTarget, string key, ValidationResults validationResults)
        {
            // Do not call DoValidate(object...) overload to prevent boxing of value types and loss of type information for the 'is' operator.
            if ((objectToValidate is T) == Negated)
                LogValidationResult(validationResults, GetMessage(objectToValidate, key), currentTarget, key);
        }

        /// <summary>
        /// Implements the validation logic for the receiver.
        /// </summary>
        /// <param name="objectToValidate">The object to validate.</param>
        /// <param name="currentTarget">The object on the behalf of which the validation is performed.</param>
        /// <param name="key">The key that identifies the source of objectToValidate.</param>
        /// <param name="validationResults">The validation results to which the outcome of the validation should be stored.</param>
        protected override void DoValidate(object objectToValidate, object currentTarget, string key, ValidationResults validationResults)
        {
            if((objectToValidate is T) == Negated)
                LogValidationResult(validationResults, GetMessage(objectToValidate, key), currentTarget, key);
        }

        /// <summary>
        /// Gets the message representing a failed validation.
        /// </summary>
        /// <param name="objectToValidate">The object for which validation was performed.</param>
        /// <param name="key">The key representing the value being validated for <paramref name="objectToValidate"/>.</param>
        /// <returns>The message representing the validation failure.</returns>
        protected override string GetMessage(object objectToValidate, string key)
        {
            return string.Format(CultureInfo.CurrentUICulture,
                this.MessageTemplate,
                objectToValidate,
                key,
                this.Tag,
                objectToValidate == null ? "(null)" : objectToValidate.GetType().ToString(),
                typeof(T).ToString());
        }

        #endregion Methods
    }
}
